Index
Dieses Datenprjekt entstand als Abschlussprojekt zum Kurs “Daten Visualisieren” der Uni Regensburg.
1 Unfalldaten
Das Statistische Bundeamt stellt eine vielzahl an unterschiedlichen Datensätzen zur Verfügung. In diesem Dokument werden offizielle Unfalldaten mit Personenschaden für Regensburg ausgewertet. Diese können hier heruntergeladen werden.
library(tidyverse)
library(lubridate)filenames <-
list.files(
path = here::here("data-raw/accidents")
)ReadGarbageData <- function(filename){
# read a file
data <- read_csv2(here::here("data-raw/accidents", filename))
# the files have different headers
# this key corrects that
col_key <-
c(
# ids
FID = "id1",
OBJECTID = "id2",
OBJECTID_1 = "id2",
UIDENTSTLA = "id3",
UIDENTSTLAE = "id3",
# lighting
ULICHTVERH = "light_condition",
LICHT = "light_condition",
# street condition
IstStrasse = "street_condition",
STRZUSTAND = "street_condition",
# other
IstSonstig = "other",
IstSonstige = "other",
# common
ULAND = "land",
UREGBEZ = "bezirk",
UKREIS = "kreis",
UGEMEINDE = "gemeinde",
UJAHR = "year",
UMONAT = "month",
USTUNDE = "hour",
UWOCHENTAG = "weekday",
UKATEGORIE = "severity",
UART = "kind_of_accident",
UTYP1 = "type_of_accident",
IstRad = "bicycle",
IstKrad = "bike",
IstPKW = "car",
IstFuss = "pedestrian",
IstGkfz = "truck",
LINREFX = "linref_x",
LINREFY = "linref_y",
XGCSWGS84 = "lng",
YGCSWGS84 = "lat"
)
# correct col names via the key
names(data) <- col_key[names(data)]
# correct col types
data <-
data |>
mutate(
bezirk = as.character(bezirk),
year = as.numeric(year),
month = as.numeric(month),
hour = as.numeric(hour)
)
return(data)
}data <-
filenames |>
map_dfr(
ReadGarbageData
) |>
select(-starts_with("id"))data <-
data |>
filter(
land == "09" &
bezirk == "3" &
kreis == "62" &
gemeinde == "000"
) |>
select(-kind_of_accident, -type_of_accident, -linref_x, -linref_y) |>
select(-land, -bezirk, -kreis, -gemeinde)
# add id
data <-
data |>
mutate(
id = row_number()
) |>
select(id, everything())data <-
data |>
mutate(
datetime = glue::glue("{month}-{year}-{hour}") |>
parse_datetime(format = "%m-%Y-%H")
) |>
mutate(
weekday = wday(weekday, label = TRUE),
date = date(datetime)
) |>
mutate(
across(
.cols = c(severity, light_condition, street_condition),
.fns = as_factor
)
) |>
mutate(
across(
.cols = bicycle:other,
.fns = as.logical
)
) |>
mutate(
severity = fct_recode(
severity,
"Toedlich" = "1",
"Schwer" = "2",
"Leicht" = "3"
),
light_condition = fct_recode(
light_condition,
"Tageslicht" = "0",
"Dämmerung" = "1",
"Dunkelheit" = "2"
),
street_condition = fct_recode(
street_condition,
"Trocken" = "0",
"Nass/Feucht" = "1",
"Winterglatt" = "2"
)
)data |>
DT::datatable()1.1 Geocode
Der folgende Chunk fügt den einzelnen Unfällen die passende Adresse hinzu. Dies dauert wegen fehlender Parallelisierung recht lange (1-2 Stunden), und wurde in der Auswertung nicht wirklich benötigt. Daher wird der Code nicht ausgeführt.
pb <-
progress::progress_bar$new(
format = "Lade Geodaten :current/:total [:bar] :percent (eta: :eta)",
total = nrow(data)
)
pb$tick(0)
data <-
map2_dfr(
.x = data$lng,
.y = data$lat,
.f = function(x = .x, y = .y){
geodata <- photon::reverse(x, y) |>
select(name:country)
pb$tick()
return(geodata)
}
) |>
mutate(
id = row_number(),
street = ifelse(is.na(street), name, street)
) |>
right_join(data, by = c("id"))
remove(pb)1.2 CSV/RDA speichern.
# data
write_csv2(
x = data,
file = here::here("output/regensburg_data.csv")
)
save(
list = c("data"),
file = here::here("data/regensburg_data.rda")
)2 Shapefiles
library(tidyverse)
library(sf)Die verwendeten Shapefiles (Stadtgrenze, Stadtteile, Gewässer, Autobahnen) stammen vom Amt für Stadtentwicklung Regensburg
2.1 Stadtgrenze Regensburg
sf.regensburg <-
read_sf(here::here("data-raw/shapefiles/regensburg/gesamtstadt.shp")) |>
st_transform("WGS84") |>
rename(
"m2" = qm
) |>
select(m2, geometry)ggplot() +
geom_sf(data = sf.regensburg) +
ggthemes::theme_map()2.2 Stadtteile
sf.districts <-
read_sf(here::here("data-raw/shapefiles/districts/stadtbezirke.shp")) |>
st_transform("WGS84") |>
rename(
"district" = Name,
"ha" = Hektar
) |>
mutate(
m2 = ha * 10^4
) |>
select(district, m2, geometry)ggplot() +
geom_sf(data = sf.districts, linetype = 2) +
geom_sf(data = sf.regensburg, lwd = 1, alpha = 0) +
ggthemes::theme_map()2.3 Autobahnen
sf.highways <-
read_sf(here::here("data-raw/shapefiles/highways/autobahn.shp")) |>
st_transform("WGS84") |>
rename(
"feeder" = ZUBRINGER
) |>
mutate(
feeder = case_when(
feeder == "j" ~ TRUE,
feeder == "n" ~ FALSE
)
)ggplot() +
geom_sf(data = sf.districts, linetype = 2) +
geom_sf(data = sf.highways, alpha = 0.6) +
geom_sf(data = sf.regensburg, lwd = 1, alpha = 0) +
ggthemes::theme_map()2.4 Flüsse
sf.rivers <-
read_sf(here::here("data-raw/shapefiles/rivers/gewaesser.shp")) |>
st_transform("WGS84") |>
select(geometry)ggplot() +
geom_sf(data = sf.districts, linetype = 2) +
geom_sf(data = sf.rivers, alpha = 0.6) +
geom_sf(data = sf.highways, alpha = 0.6) +
geom_sf(data = sf.regensburg, lwd = 1, alpha = 0) +
ggthemes::theme_map()2.5 Finale Karte
ggplot() +
geom_sf(data = sf.districts, aes(fill = district), alpha = 0.7) +
geom_sf(data = sf.rivers, alpha = 0.7, fill = "lightblue") +
geom_sf(data = sf.regensburg, lwd = 1, alpha = 0) +
theme_void() +
theme(
legend.position = "right",
legend.title = element_blank()
)3 Leaflet Karte
Teil der Aufgabenstellung war der Export einer mit Leaflet erstellten Karte mit geclusterten Punkten. Das Ergebnis dieses Kapitels wird für die Abgabe durch ein externes Script ausgeführt, das die Karte als HTML-Datei speichert.
library(tidyverse)
library(leaflet)
library(sf)sf.data <-
data |>
st_as_sf(coords = c("lng", "lat"), crs = "WGS84")3.1 Basemap
bounds <- sf.regensburg |> st_bbox()
map <-
leaflet(
options = leafletOptions(
crs = leafletCRS(code = "WGS84"),
preferCanvas = NULL
)
) |>
addProviderTiles(
provider = providers$OpenStreetMap.DE,
group = "OSM",
options = providerTileOptions(minZoom = 11)
) |>
setView(
lng = (as.numeric(bounds[1]) + as.numeric(bounds[3]))/2,
lat = (as.numeric(bounds[2]) + as.numeric(bounds[4]))/2,
zoom = 12
) |>
setMaxBounds(
lng1 = as.numeric(bounds[1] - 0.015),
lat1 = as.numeric(bounds[2] - 0.015),
lng2 = as.numeric(bounds[3] + 0.015),
lat2 = as.numeric(bounds[4] + 0.015)
)3.2 Marker
custom_popup <- function(data, header) {
text <-
glue::glue(
"<b>{header}</b> ",
"<br>",
"{data$month}/{data$year} ({data$hour} Uhr)"
)
return(text)
}map <-
map |>
addAwesomeMarkers(
data = data |> filter(severity == "Toedlich"),
group = "Tödliche Unfälle",
lng = ~lng,
lat = ~lat,
icon = awesomeIcons(
icon = 'ios-close',
iconColor = 'black',
library = 'ion',
markerColor = "red"
),
clusterOptions = markerClusterOptions(),
popup = custom_popup(
data = data |> filter(severity == "Toedlich"),
header = "Tödlicher Unfall"
)
) |>
addAwesomeMarkers(
data = data |> filter(severity == "Schwer"),
group = "Schwere Unfälle",
lng = ~lng,
lat = ~lat,
icon = awesomeIcons(
icon = 'ios-close',
iconColor = 'black',
library = 'ion',
markerColor = "orange"
),
clusterOptions = markerClusterOptions(),
popup = custom_popup(
data = data |> filter(severity == "Schwer"),
header = "Schwerer Unfall"
)
) |>
addAwesomeMarkers(
data = data |> filter(severity == "Leicht"),
group = "Leichte Unfälle",
lng = ~lng,
lat = ~lat,
icon = awesomeIcons(
icon = 'ios-close',
iconColor = 'black',
library = 'ion',
markerColor = "beige"
),
clusterOptions = markerClusterOptions(),
popup = custom_popup(
data = data |> filter(severity == "Leicht"),
header = "Leichter Unfall"
)
)3.3 Stadtteile als Shapefile
custom_label <- function(data) {
text <- glue::glue(
"{data$district}: {data$n} Unfälle"
)
return(text)
}districts <-
data |>
st_as_sf(coords = c("lng", "lat"), crs = "WGS84") |>
rename(
points = geometry
) |>
st_join(
y = sf.districts |> rename("district_shape" = geometry),
join = st_within,
left = TRUE
) |>
select(-m2) |>
as_tibble() |>
left_join(
y = sf.districts |> rename("district_polygon" = geometry) ,
by = "district"
) |>
drop_na(district) |>
mutate(
district = as_factor(district) |>
fct_infreq() |>
fct_rev()
) |>
add_count(district) |>
select(district, district_polygon, n) |>
unique() |>
st_as_sf()map <-
map |>
addPolygons(
data = districts,
group = "Stadtteile",
opacity = 1,
weight = 0.5,
fillOpacity = 0.5,
color = "black",
fillColor = ~colorNumeric("viridis", n)(n),
highlightOptions = highlightOptions(
color = "white",
weight = 2,
bringToFront = TRUE
),
label = ~custom_label(data = districts)
)3.4 Bedienelemente
map <-
map |>
addProviderTiles(
provider = providers$Stamen.TonerBackground,
group = "Stadtteile",
options = providerTileOptions(minZoom = 11)
) |>
addLayersControl(
baseGroups = c("OSM", "Stadtteile"),
overlayGroups = c("Tödliche Unfälle", "Schwere Unfälle", "Leichte Unfälle"),
options = layersControlOptions(collapsed = FALSE)
)3.5 Finale Karte
map4 Auswertung
Zur besseren Lesbarkeit wird der R Code in diesem Kapitel nicht gezeigt. Dieser besteht größtenteils aus Plots und ist bis auf wenige Ausnahmen nicht weiter relevant.
Im Stadtgebiet Regensburg geschahen von den Jahren 2016 bis 2020 insgesamt 3167 Unfälle mit Personenschaden. Abbildung 4.1 zeigt die monatlichen Unfälle in diesem Zeitraum.
Abbildung 4.1: Monatliche Unfälle in Regensburg.
Während sich kein eindeutiger Auf- oder Abwärtstrend feststellen lässt, zeigen die Daten dennoch eine Jährliche Periodizität: Im Sommer finden die meisten Unfälle mit Personenschaden statt, während die Anzahl der Unfälle von Herbst bis Frühjahr sinkt.
Abbildung 4.2 zeigt die Anzahl der jährlichen Unfälle in Regensburg. Im Jahr 2020 zeigt sich ein Rückgang von 25%. Dieser könnte auf geringeren Verkehr aufgrund der Corona Pandemie zurückgeführt werden. Dies wird durch Abbildung 4.3 verdeutlicht: Alle dokumentierten Verkehrsmittel hatten einen Rückgang der jährlichen Unfälle von 2019 bis 2020.
Abbildung 4.2: Jährliche Unfälle mit Personenschaden.
Abbildung 4.3 zeigt zudem, dass sich die Anzahl der Unfälle aller Verkehrsmittel außer Fahrrad auf einem fallenden Trend befinden. Die Anzahl der Unfälle mit Fahrradbeteiligung dagegen stieg bis 2020 kontinuierlich an.
Abbildung 4.3: Jährliche Unfälle mit Personenschaden nach Verkehrsteilnehmer unterteilt.
Der wichtigste Faktor, der die Unfälle unterscheidet, ist die Schwere der schlimmsten Verletzung (Leicht, Schwer, Tödlich). Ungefähr 87% Unfälle gehen mit höchstens leichten Verletzungen aus. Lediglich 0% der Unfälle enden tödlich.
4.1 Unfälle nach Monat
Abbildung 4.4 zeigt die monatliche Verteilung der dokumentierten Unfälle. Es lässt sich ablesen, dass die Anzahl der Unfälle vom Winter bis Sommer ansteigt und in Juli ein Maximum annimmt. Mit dem Beginn der Sommerferien in Bayern im August sinkt die Anzahlt der Unfälle dann um 36%.
Abbildung 4.4: Monatiliche Unfälle mit Personenschaden. A) Schwere: Über das Jahr hinweg gibt es kaum relative Unterschiede. B) Lichtverhältnisse: Über die Wintermonate nehmen die Unfälle bei Dunkelheit zu. C) Die Unfälle bei Nässe nehmen von Herbst bis Winter zu. Vom Frühjahr bis Sommer nimmt die Anzahl an Unfällen bei Nässe ab.
Die Schwere der Unfälle unterscheidet sich zwischen den Jahreszeiten kaum.
4.2 Unfälle nach Uhrzeit
Die meisten Unfälle mit Personenschaden finden tagsüber statt. Zusätzlich zeigt Abbildung 4.5 zwei Peaks: Um sieben Uhr und von 16-17 Uhr. Diese stimmen mit dem Arbeitsverkehr überein.
Abbildung 4.5 zeigt auch, dass Unfälle bei Dunkelheit zwischen 16 und 6 Uhr stattfinden. Das ist keine überraschende Erkenntnis, stärkt aber das Vertrauen in die Zuverlässigkeit der Daten.
Abbildung 4.5: Unfälle nach Tageszeit.
4.3 Wo finden die meisten Unfälle statt?
Abbildung 4.6 zeigt die Anzahl an Unfällen von 2016 und 2020. In diesem Zeitraum haben sich die meisten Unfälle in der Regensburger Innenstadt (492) zugetragen.
Berechnet man die Unfälle pro Quadratkilometer, wird der Unterschied zwischen der Innenstadt und den Umliegenden Stadtteilen noch eindeutiger (siehe Abbildung 4.7).
Abbildung 4.6: Unfälle mit Personenschaden pro Stadtteil (Abolute Zahlen).
Abbildung 4.7: Unfälle mit Personenschaden pro Quadratkilometer.
4.4 Welches ist das gefährlichste Verkehrsmittel
Betrachtet man die absolute Anzahl der Unfälle mit Personenschaden (siehe Abbildung 4.8), kommt der Eindruck zustande, dass das Auto das gefährlichste Verkehrsmittel ist. Normiert man die Daten (sieh Abbildung 4.9), zeigt sich dagegen, dass das Auto relativ sicher ist. So besitzt es das geringste Verhältnis zwischen schweren/tödlichen und leichten Unfällen.
Abbildung 4.8: Unfälle nach Verkehrsteilnehmer.
Abbildung 4.9: Unfälle nach Verkehrsteilnehmer (normiert).
In Relation zur Gesamtanzahl der Unfälle mit Personenschaden der einzelnen Verkehrsmittel, sind an den meisten tödlichen Unfällen Motorradfahrer beteiligt (siehe 4.10).
Abbildung 4.10: Anteil der tödlichen Unfälle nach Verkehrsteilnehmer.
Trotz der geringeren Gesamtanzahl an Unfällen, ist das Motorrad somit das gefährlichste Verkehrsmittel.
Sonstiges
A Dashboard
Die Daten dieses Projekts wurden verwendet, um ein Dashboard zu erstellen. Dieses wurde mit dem Paket flexdashboard erstellt. shiny verleiht dem Dashboard Interaktivität.
Eine Liveversion der App kann hier gefunden werden.
B Daten
load(
here::here("data/regensburg_data.rda")
)data |>
DT::datatable()